<template>
  <div>
    <div class="uploader" @dragover.prevent="handleDragOver" @drop.prevent="handleDrop">
      drop here
    </div>
    <ul>
      <li v-for="(item, i) in fileList" :key="i">{{ item.path }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const fileList = ref([]);

function handleDragOver(e) {
  e.dataTransfer.dropEffect = 'copy';
}

async function getDirFiles(entries, customConfig) {
  const config = {
    maxDeep: 30,
    maxSize: 128 * 1024 * 1024,
    maxTotal: 1000,
    bfs: false,
    ...customConfig,
  };
  const fileList = [];
  const queue = [entries];
  if (config.bfs) {
    await _bfsDirFiles(queue, config, fileList);
  } else {
    await _dfsDirFiles(entries, 1, config, fileList);
  }
  return fileList;
}

async function _dfsDirFiles(entries, deep, config, fileList) {
  // 遍历当前所有文件
  const fileEntriesPromises = Array.from(entries)
    .filter((entry) => entry.isFile)
    .map((entry) => {
      return new Promise((resolve) => {
        entry.file(
          (f) => {
            if (f.size <= config.maxSize && fileList.length < config.maxTotal) {
              fileList.push({ path: entry.fullPath, file: f });
            }
            resolve();
          },
          (err) => {
            resolve();
            console.log(err);
          }
        );
      });
    });
  await Promise.all(fileEntriesPromises);

  // 依次遍历剩余目录下文件
  if (deep < config.maxDeep && fileList.length < config.maxTotal) {
    const directoryEntriesPromises = Array.from(entries)
      .filter((entry) => entry.isDirectory)
      .map((entry) => {
        let dirReader = entry.createReader(); //创建目录读取对象
        return new Promise((resolve) => {
          dirReader.readEntries((entries) => {
            resolve(entries);
          });
        });
      });
    for (let i = 0; i < directoryEntriesPromises.length; i++) {
      const entries = await directoryEntriesPromises[i];
      await _dfsDirFiles(entries, deep + 1, config, fileList);
    }
  }
}

async function _bfsDirFiles(queue, config, fileList) {
  // 广度优先遍历
  let deep = 1;
  while (queue.length > 0) {
    // 取出队头元素
    const entries = queue.shift();
    // 遍历当前所有文件
    const fileEntriesPromises = Array.from(entries)
      .filter((entry) => entry.isFile)
      .map((entry) => {
        return new Promise((resolve) => {
          entry.file(
            (f) => {
              if (f.size <= config.maxSize && fileList.length < config.maxTotal) {
                fileList.push({ path: entry.fullPath, file: f });
              }
              resolve();
            },
            (err) => {
              resolve();
              console.log(err);
            }
          );
        });
      });
    await Promise.all(fileEntriesPromises);
    // 依次遍历剩余目录下文件
    if (deep < config.maxDeep && fileList.length < config.maxTotal) {
      const directoryEntriesPromises = Array.from(entries)
        .filter((entry) => entry.isDirectory)
        .map((entry) => {
          let dirReader = entry.createReader(); //创建目录读取对象
          return new Promise((resolve) => {
            dirReader.readEntries((entries) => {
              // 有bug无法获取全部entry
              queue.push(entries);
              resolve();
            });
          });
        });
      await Promise.all(directoryEntriesPromises);
      deep += 1;
    }
  }
}

async function handleDrop(e) {
  const entries = Array.from(e.dataTransfer.items).map((item) => item.webkitGetAsEntry());
  const res = await getDirFiles(entries, { maxTotal: 10 });
  fileList.value = fileList.value.concat(res);
  console.log(fileList.value);
}
</script>

<style scoped>
.uploader {
  height: 300px;
  width: 300px;
  border-radius: 8px;
  border: 1px dashed #ccc;
  text-align: center;
  line-height: 300px;
}
</style>
